msg_tool\scripts\entis_gls/
srcxml.rs

1//! Entis GLS engine XML Script (.srcxml)
2use crate::ext::io::*;
3use crate::ext::rcdom::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use crate::utils::encoding::*;
7use anyhow::Result;
8use markup5ever_rcdom::{Handle, RcDom, SerializableHandle};
9use xml5ever::driver::parse_document;
10use xml5ever::serialize::serialize;
11use xml5ever::tendril::TendrilSink;
12
13#[derive(Debug)]
14/// A builder for Entis GLS srcxml scripts.
15pub struct SrcXmlScriptBuilder {}
16
17impl SrcXmlScriptBuilder {
18    /// Creates a new instance of `SrcXmlScriptBuilder`.
19    pub fn new() -> Self {
20        Self {}
21    }
22}
23
24impl ScriptBuilder for SrcXmlScriptBuilder {
25    fn default_encoding(&self) -> Encoding {
26        Encoding::Utf8
27    }
28
29    fn build_script(
30        &self,
31        buf: Vec<u8>,
32        _filename: &str,
33        encoding: Encoding,
34        _archive_encoding: Encoding,
35        config: &ExtraConfig,
36        _archive: Option<&Box<dyn Script>>,
37    ) -> Result<Box<dyn Script>> {
38        Ok(Box::new(SrcXmlScript::new(buf, encoding, config)?))
39    }
40
41    fn extensions(&self) -> &'static [&'static str] {
42        &["srcxml"]
43    }
44
45    fn script_type(&self) -> &'static ScriptType {
46        &ScriptType::EntisGls
47    }
48}
49
50#[derive(Debug)]
51/// Entis GLS engine XML Script
52pub struct SrcXmlScript {
53    handle: Handle,
54    lang: Option<String>,
55}
56
57impl SrcXmlScript {
58    /// Creates a new `SrcXmlScript` from the provided buffer and encoding.
59    ///
60    /// * `buf` - The buffer containing the XML data.
61    /// * `encoding` - The encoding of the XML data.
62    /// * `config` - Additional configuration options.
63    pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
64        let decoded = decode_to_string(encoding, &buf, false)?;
65        let dom = parse_document(RcDom::default(), Default::default())
66            .from_utf8()
67            .one(decoded.as_bytes());
68        {
69            let error = dom.errors.try_borrow()?;
70            for e in error.iter() {
71                eprintln!("WARN: Error parsing srcxml: {}", e);
72                crate::COUNTER.inc_warning();
73            }
74        }
75        Ok(Self {
76            handle: dom.document,
77            lang: config.entis_gls_srcxml_lang.clone(),
78        })
79    }
80}
81
82impl Script for SrcXmlScript {
83    fn default_output_script_type(&self) -> OutputScriptType {
84        OutputScriptType::Json
85    }
86
87    fn default_format_type(&self) -> FormatOptions {
88        FormatOptions::None
89    }
90
91    fn extract_messages(&self) -> Result<Vec<Message>> {
92        let mut messages = Vec::new();
93        let mut lang = self.lang.clone();
94        for i in self.handle.children.try_borrow()?.iter() {
95            if i.is_element("xscript") {
96                for code in i.children.try_borrow()?.iter() {
97                    if code.is_element("code") {
98                        for ins in code.children.try_borrow()?.iter() {
99                            if ins.is_element("msg") {
100                                let lan = match lang.as_ref() {
101                                    Some(l) => l.as_str(),
102                                    None => {
103                                        for attr in ins.element_attr_keys()? {
104                                            if attr.starts_with("name_")
105                                                || attr.starts_with("text_")
106                                            {
107                                                lang = Some(attr[5..].to_string());
108                                                break;
109                                            }
110                                        }
111                                        lang.as_ref().map(|s| s.as_str()).unwrap_or("")
112                                    }
113                                };
114                                let name_ref = if lan.is_empty() {
115                                    "name"
116                                } else {
117                                    &format!("name_{}", lan)
118                                };
119                                let mut name = ins.get_attr_value(name_ref)?;
120                                if name.as_ref().is_some_and(|s| s.is_empty()) {
121                                    name = None;
122                                }
123                                let text_ref = if lan.is_empty() {
124                                    "text"
125                                } else {
126                                    &format!("text_{}", lan)
127                                };
128                                let message = ins
129                                    .get_attr_value(text_ref)?
130                                    .ok_or(anyhow::anyhow!("text not found"))?;
131                                messages.push(Message { name, message })
132                            } else if ins.is_element("select") {
133                                for menu in ins.children.try_borrow()?.iter() {
134                                    if menu.is_element("menu") {
135                                        let lan = match lang.as_ref() {
136                                            Some(l) => l.as_str(),
137                                            None => {
138                                                for attr in ins.element_attr_keys()? {
139                                                    if attr.starts_with("name_")
140                                                        || attr.starts_with("text_")
141                                                    {
142                                                        lang = Some(attr[5..].to_string());
143                                                        break;
144                                                    }
145                                                }
146                                                lang.as_ref().map(|s| s.as_str()).unwrap_or("")
147                                            }
148                                        };
149                                        let text_ref = if lan.is_empty() {
150                                            "text"
151                                        } else {
152                                            &format!("text_{}", lan)
153                                        };
154                                        let message = menu
155                                            .get_attr_value(text_ref)?
156                                            .ok_or(anyhow::anyhow!("text not found"))?;
157                                        messages.push(Message {
158                                            name: None,
159                                            message,
160                                        });
161                                    }
162                                }
163                            }
164                        }
165                    }
166                }
167            }
168        }
169        Ok(messages)
170    }
171
172    fn import_messages<'a>(
173        &'a self,
174        messages: Vec<Message>,
175        mut file: Box<dyn WriteSeek + 'a>,
176        _filename: &str,
177        encoding: Encoding,
178        replacement: Option<&'a ReplacementTable>,
179    ) -> Result<()> {
180        let root = self.handle.deep_clone(None)?;
181        if !encoding.is_utf8() {
182            let len = root.children.try_borrow()?.len();
183            if len > 0 && root.children.try_borrow()?[0].is_processing_instruction("xml") {
184                root.change_child(0, |data| {
185                    data.set_processing_instruction_content("version=\"1.0\"")
186                })?;
187            }
188        }
189        let mut lang = self.lang.clone();
190        let mut mess = messages.iter();
191        let mut mes = mess.next();
192        for i in root.children.try_borrow()?.iter() {
193            if i.is_element("xscript") {
194                for code in i.children.try_borrow()?.iter() {
195                    if code.is_element("code") {
196                        for ins in code.children.try_borrow()?.iter() {
197                            if ins.is_element("msg") {
198                                let m = match mes {
199                                    Some(m) => m,
200                                    None => {
201                                        return Err(anyhow::anyhow!(
202                                            "Not enough messages provided"
203                                        ));
204                                    }
205                                };
206                                let lan = match lang.as_ref() {
207                                    Some(l) => l.as_str(),
208                                    None => {
209                                        for attr in ins.element_attr_keys()? {
210                                            if attr.starts_with("name_")
211                                                || attr.starts_with("text_")
212                                            {
213                                                lang = Some(attr[5..].to_string());
214                                                break;
215                                            }
216                                        }
217                                        if lang.is_none() {
218                                            lang = Some(String::new());
219                                        }
220                                        lang.as_ref().map(|s| s.as_str()).unwrap_or("")
221                                    }
222                                };
223                                let name_ref = if lan.is_empty() {
224                                    "name"
225                                } else {
226                                    &format!("name_{}", lan)
227                                };
228                                let name = match &m.name {
229                                    Some(name) => {
230                                        let mut name = name.clone();
231                                        if let Some(repl) = replacement {
232                                            for (k, v) in &repl.map {
233                                                name = name.replace(k, v);
234                                            }
235                                        }
236                                        name
237                                    }
238                                    None => String::new(),
239                                };
240                                ins.set_attr_value(name_ref, &name)?;
241                                let message = m.message.clone();
242                                let text_ref = if lan.is_empty() {
243                                    "text"
244                                } else {
245                                    &format!("text_{}", lan)
246                                };
247                                ins.set_attr_value(text_ref, &message)?;
248                                mes = mess.next();
249                            } else if ins.is_element("select") {
250                                for menu in ins.children.try_borrow()?.iter() {
251                                    if menu.is_element("menu") {
252                                        let m = match mes {
253                                            Some(m) => m,
254                                            None => {
255                                                return Err(anyhow::anyhow!(
256                                                    "Not enough messages provided"
257                                                ));
258                                            }
259                                        };
260                                        let lan = match lang.as_ref() {
261                                            Some(l) => l.as_str(),
262                                            None => {
263                                                for attr in ins.element_attr_keys()? {
264                                                    if attr.starts_with("name_")
265                                                        || attr.starts_with("text_")
266                                                    {
267                                                        lang = Some(attr[5..].to_string());
268                                                        break;
269                                                    }
270                                                }
271                                                if lang.is_none() {
272                                                    lang = Some(String::new());
273                                                }
274                                                lang.as_ref().map(|s| s.as_str()).unwrap_or("")
275                                            }
276                                        };
277                                        let text_ref = if lan.is_empty() {
278                                            "text"
279                                        } else {
280                                            &format!("text_{}", lan)
281                                        };
282                                        let mut message = m.message.clone();
283                                        if let Some(repl) = replacement {
284                                            for (k, v) in &repl.map {
285                                                message = message.replace(k, v);
286                                            }
287                                        }
288                                        menu.set_attr_value(text_ref, &message)?;
289                                        mes = mess.next();
290                                    }
291                                }
292                            }
293                        }
294                    }
295                }
296            }
297        }
298        let doc: SerializableHandle = root.clone().into();
299        let mut output = MemWriter::new();
300        serialize(&mut output, &doc, Default::default())
301            .map_err(|e| anyhow::anyhow!("Error serializing srcxml: {}", e))?;
302        if encoding.is_utf8() {
303            file.write_all(&output.data)?;
304            return Ok(());
305        }
306        let s = decode_to_string(Encoding::Utf8, &output.data, true)?;
307        let encoded = encode_string(encoding, &s, false)?;
308        file.write_all(&encoded)?;
309        Ok(())
310    }
311}